Skip to main content

agent_os_kernel/
user.rs

1#[derive(Debug, Clone, PartialEq, Eq)]
2pub struct ProcessIdentity {
3    pub uid: u32,
4    pub gid: u32,
5    pub euid: u32,
6    pub egid: u32,
7    pub supplementary_gids: Vec<u32>,
8}
9
10impl Default for ProcessIdentity {
11    fn default() -> Self {
12        Self {
13            uid: 1000,
14            gid: 1000,
15            euid: 1000,
16            egid: 1000,
17            supplementary_gids: vec![1000],
18        }
19    }
20}
21
22#[derive(Debug, Clone, Default, PartialEq, Eq)]
23pub struct UserConfig {
24    pub uid: Option<u32>,
25    pub gid: Option<u32>,
26    pub euid: Option<u32>,
27    pub egid: Option<u32>,
28    pub username: Option<String>,
29    pub homedir: Option<String>,
30    pub shell: Option<String>,
31    pub gecos: Option<String>,
32    pub group_name: Option<String>,
33    pub supplementary_gids: Vec<u32>,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct UserManager {
38    pub uid: u32,
39    pub gid: u32,
40    pub euid: u32,
41    pub egid: u32,
42    pub username: String,
43    pub homedir: String,
44    pub shell: String,
45    pub gecos: String,
46    pub group_name: String,
47    pub supplementary_gids: Vec<u32>,
48}
49
50impl Default for UserManager {
51    fn default() -> Self {
52        Self::from_config(UserConfig::default())
53    }
54}
55
56impl UserManager {
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    pub fn from_config(config: UserConfig) -> Self {
62        let uid = config.uid.unwrap_or(1000);
63        let gid = config.gid.unwrap_or(1000);
64        let username = config.username.unwrap_or_else(|| String::from("user"));
65        let supplementary_gids = normalize_supplementary_gids(gid, config.supplementary_gids);
66
67        Self {
68            uid,
69            gid,
70            euid: config.euid.unwrap_or(uid),
71            egid: config.egid.unwrap_or(gid),
72            username: username.clone(),
73            homedir: config.homedir.unwrap_or_else(|| String::from("/home/user")),
74            shell: config.shell.unwrap_or_else(|| String::from("/bin/sh")),
75            gecos: config.gecos.unwrap_or_default(),
76            group_name: config.group_name.unwrap_or(username),
77            supplementary_gids,
78        }
79    }
80
81    pub fn identity(&self) -> ProcessIdentity {
82        ProcessIdentity {
83            uid: self.uid,
84            gid: self.gid,
85            euid: self.euid,
86            egid: self.egid,
87            supplementary_gids: self.supplementary_gids.clone(),
88        }
89    }
90
91    pub fn getgroups(&self) -> Vec<u32> {
92        self.supplementary_gids.clone()
93    }
94
95    pub fn getpwuid(&self, uid: u32) -> Option<String> {
96        if uid == self.uid {
97            return Some(format!(
98                "{}:x:{}:{}:{}:{}:{}",
99                self.username, self.uid, self.gid, self.gecos, self.homedir, self.shell
100            ));
101        }
102
103        None
104    }
105
106    pub fn getgrgid(&self, gid: u32) -> Option<String> {
107        if gid == self.gid {
108            return Some(format!(
109                "{}:x:{}:{}",
110                self.group_name, self.gid, self.username
111            ));
112        }
113
114        if self.supplementary_gids.contains(&gid) {
115            let group_name = format!("group{gid}");
116            return Some(format!("{group_name}:x:{gid}:{}", self.username));
117        }
118
119        None
120    }
121}
122
123fn normalize_supplementary_gids(primary_gid: u32, supplementary_gids: Vec<u32>) -> Vec<u32> {
124    let mut normalized = Vec::with_capacity(supplementary_gids.len() + 1);
125    normalized.push(primary_gid);
126    for gid in supplementary_gids {
127        if !normalized.contains(&gid) {
128            normalized.push(gid);
129        }
130    }
131    normalized
132}