Subramanya97 commited on
Commit
4b8758b
Β·
1 Parent(s): 9aa9297

Deploy from markdownfs@1a8d486

Browse files
src/auth/session.rs CHANGED
@@ -1,5 +1,5 @@
1
  use super::perms::{check_permission, Access};
2
- use super::{Gid, Uid, ROOT_UID};
3
  use crate::fs::inode::Inode;
4
 
5
  /// Context for the user an agent is acting on behalf of.
@@ -105,10 +105,29 @@ impl Session {
105
  }
106
  }
107
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  /// Whether either the principal or (if delegating) the delegate is root.
109
- /// For intersection: both must pass, so root status only helps if the OTHER side passes.
110
- /// This is used for checks like "is this session effectively root?" β€” answer: only if
111
- /// there's no delegate constraining it.
112
  pub fn is_effectively_root(&self) -> bool {
113
  if self.uid != ROOT_UID {
114
  return false;
 
1
  use super::perms::{check_permission, Access};
2
+ use super::{Gid, Uid, ROOT_UID, WHEEL_GID};
3
  use crate::fs::inode::Inode;
4
 
5
  /// Context for the user an agent is acting on behalf of.
 
105
  }
106
  }
107
 
108
+ /// Whether the principal is a wheel-group member (admin-equivalent).
109
+ pub fn is_wheel(&self) -> bool {
110
+ self.groups.contains(&WHEEL_GID)
111
+ }
112
+
113
+ /// Whether the session may bypass per-file Read checks (i.e., see every
114
+ /// directory entry and read every file). True for literal uid=0 or any
115
+ /// wheel-group member. Privileged write ops (chmod-across-owner,
116
+ /// chown-uid) still go through the stricter `is_effectively_root` check.
117
+ /// When delegating, the delegate must also be admin-equivalent.
118
+ pub fn can_read_anywhere(&self) -> bool {
119
+ let principal_admin = self.uid == ROOT_UID || self.is_wheel();
120
+ if !principal_admin {
121
+ return false;
122
+ }
123
+ match &self.delegate {
124
+ Some(d) => d.uid == ROOT_UID || d.groups.contains(&WHEEL_GID),
125
+ None => true,
126
+ }
127
+ }
128
+
129
  /// Whether either the principal or (if delegating) the delegate is root.
130
+ /// Strict: only literal uid=0 qualifies. Used by privileged write ops.
 
 
131
  pub fn is_effectively_root(&self) -> bool {
132
  if self.uid != ROOT_UID {
133
  return false;
src/db.rs CHANGED
@@ -683,7 +683,13 @@ impl MarkdownDb {
683
  .map(|u| u.groups[0])
684
  .unwrap_or(0);
685
  let home = format!("/home/{name}");
 
686
  let _ = guard.fs.mkdir_p(&home, uid, gid);
 
 
 
 
 
687
  // Lock the home dir down: only the owner can list/enter it.
688
  let _ = guard.fs.chmod(&home, 0o700);
689
  drop(guard);
 
683
  .map(|u| u.groups[0])
684
  .unwrap_or(0);
685
  let home = format!("/home/{name}");
686
+ let already_exists = guard.fs.stat(&home).is_ok();
687
  let _ = guard.fs.mkdir_p(&home, uid, gid);
688
+ // If the home directory was orphaned by a prior user with the same
689
+ // name (and a now-stale uid), reclaim it for the new account.
690
+ if already_exists {
691
+ let _ = guard.fs.chown(&home, uid, gid);
692
+ }
693
  // Lock the home dir down: only the owner can list/enter it.
694
  let _ = guard.fs.chmod(&home, 0o700);
695
  drop(guard);
src/fs/mod.rs CHANGED
@@ -1250,6 +1250,9 @@ impl VirtualFs {
1250
  Some(s) => s,
1251
  None => return true,
1252
  };
 
 
 
1253
  let inode = match self.get_inode(id) {
1254
  Ok(i) => i,
1255
  Err(_) => return false,
 
1250
  Some(s) => s,
1251
  None => return true,
1252
  };
1253
+ if session.can_read_anywhere() {
1254
+ return self.get_inode(id).is_ok();
1255
+ }
1256
  let inode = match self.get_inode(id) {
1257
  Ok(i) => i,
1258
  Err(_) => return false,
src/server/perms.rs CHANGED
@@ -23,7 +23,9 @@ pub async fn require_perm(
23
  path: &str,
24
  access: Access,
25
  ) -> Result<bool, VfsError> {
26
- if session.is_effectively_root() {
 
 
27
  return Ok(db.stat(path).await.is_ok());
28
  }
29
  match db.stat(path).await {
 
23
  path: &str,
24
  access: Access,
25
  ) -> Result<bool, VfsError> {
26
+ if session.is_effectively_root()
27
+ || (matches!(access, Access::Read | Access::Execute) && session.can_read_anywhere())
28
+ {
29
  return Ok(db.stat(path).await.is_ok());
30
  }
31
  match db.stat(path).await {
src/server/routes_fs.rs CHANGED
@@ -86,6 +86,7 @@ async fn get_fs_root(State(state): State<AppState>, headers: HeaderMap) -> impl
86
  .into_iter()
87
  .filter(|e| {
88
  session.is_effectively_root()
 
89
  || session.has_permission_bits(e.mode, e.uid, e.gid, Access::Read)
90
  })
91
  .collect();
 
86
  .into_iter()
87
  .filter(|e| {
88
  session.is_effectively_root()
89
+ || session.can_read_anywhere()
90
  || session.has_permission_bits(e.mode, e.uid, e.gid, Access::Read)
91
  })
92
  .collect();
tests/permissions/main.rs CHANGED
@@ -55,7 +55,7 @@ fn try_run(line: &str, fs: &mut VirtualFs, session: &mut Session) -> Result<Stri
55
  ///
56
  /// Users:
57
  /// - root (uid=0) β€” superuser
58
- /// - alice (uid=1) β€” member of: alice, engineering, wheel
59
  /// - bob (uid=2) β€” member of: bob, engineering
60
  /// - carol (uid=3) β€” member of: carol, finance
61
  /// - agent-x (uid=4) β€” agent, member of: agent-x, engineering
@@ -75,7 +75,6 @@ fn setup() -> (VirtualFs, Session, Session, Session, Session, Session) {
75
 
76
  // Group memberships
77
  run("usermod -aG engineering alice", &mut fs, &mut root);
78
- run("usermod -aG wheel alice", &mut fs, &mut root);
79
  run("usermod -aG engineering bob", &mut fs, &mut root);
80
  run("usermod -aG finance carol", &mut fs, &mut root);
81
  run("usermod -aG engineering agent-x", &mut fs, &mut root);
 
55
  ///
56
  /// Users:
57
  /// - root (uid=0) β€” superuser
58
+ /// - alice (uid=1) β€” member of: alice, engineering (regular user; wheel = admin)
59
  /// - bob (uid=2) β€” member of: bob, engineering
60
  /// - carol (uid=3) β€” member of: carol, finance
61
  /// - agent-x (uid=4) β€” agent, member of: agent-x, engineering
 
75
 
76
  // Group memberships
77
  run("usermod -aG engineering alice", &mut fs, &mut root);
 
78
  run("usermod -aG engineering bob", &mut fs, &mut root);
79
  run("usermod -aG finance carol", &mut fs, &mut root);
80
  run("usermod -aG engineering agent-x", &mut fs, &mut root);