]>
Commit | Line | Data |
---|---|---|
423e6561 DM |
1 | //! Cached user info for fast ACL permission checks |
2 | ||
6e695960 | 3 | use std::sync::{RwLock, Arc}; |
423e6561 | 4 | |
a737179e | 5 | use anyhow::{Error, bail}; |
423e6561 DM |
6 | |
7 | use proxmox::api::section_config::SectionConfigData; | |
6e695960 | 8 | use lazy_static::lazy_static; |
423e6561 DM |
9 | use proxmox::api::UserInformation; |
10 | ||
1347b115 | 11 | use super::acl::{AclTree, ROLE_NAMES, ROLE_ADMIN}; |
e6dc35ac FG |
12 | use super::user::{ApiToken, User}; |
13 | use crate::api2::types::{Authid, Userid}; | |
423e6561 | 14 | |
e6dc35ac | 15 | /// Cache User/Group/Token/Acl configuration data for fast permission tests |
423e6561 DM |
16 | pub struct CachedUserInfo { |
17 | user_cfg: Arc<SectionConfigData>, | |
18 | acl_tree: Arc<AclTree>, | |
19 | } | |
20 | ||
6e695960 DM |
21 | fn now() -> i64 { unsafe { libc::time(std::ptr::null_mut()) } } |
22 | ||
23 | struct ConfigCache { | |
24 | data: Option<Arc<CachedUserInfo>>, | |
25 | last_update: i64, | |
26 | } | |
27 | ||
28 | lazy_static! { | |
29 | static ref CACHED_CONFIG: RwLock<ConfigCache> = RwLock::new( | |
30 | ConfigCache { data: None, last_update: 0 } | |
31 | ); | |
32 | } | |
33 | ||
423e6561 DM |
34 | impl CachedUserInfo { |
35 | ||
6e695960 DM |
36 | /// Returns a cached instance (up to 5 seconds old). |
37 | pub fn new() -> Result<Arc<Self>, Error> { | |
38 | let now = now(); | |
39 | { // limit scope | |
40 | let cache = CACHED_CONFIG.read().unwrap(); | |
41 | if (now - cache.last_update) < 5 { | |
42 | if let Some(ref config) = cache.data { | |
43 | return Ok(config.clone()); | |
44 | } | |
45 | } | |
46 | } | |
47 | ||
48 | let config = Arc::new(CachedUserInfo { | |
423e6561 DM |
49 | user_cfg: super::user::cached_config()?, |
50 | acl_tree: super::acl::cached_config()?, | |
6e695960 DM |
51 | }); |
52 | ||
53 | let mut cache = CACHED_CONFIG.write().unwrap(); | |
54 | cache.last_update = now; | |
55 | cache.data = Some(config.clone()); | |
56 | ||
57 | Ok(config) | |
423e6561 DM |
58 | } |
59 | ||
e6dc35ac FG |
60 | /// Test if a authentication id is enabled and not expired |
61 | pub fn is_active_auth_id(&self, auth_id: &Authid) -> bool { | |
62 | let userid = auth_id.user(); | |
63 | ||
e7cb4dc5 | 64 | if let Ok(info) = self.user_cfg.lookup::<User>("user", userid.as_str()) { |
423e6561 DM |
65 | if !info.enable.unwrap_or(true) { |
66 | return false; | |
67 | } | |
68 | if let Some(expire) = info.expire { | |
20813274 WB |
69 | if expire > 0 && expire <= now() { |
70 | return false; | |
423e6561 DM |
71 | } |
72 | } | |
423e6561 DM |
73 | } else { |
74 | return false; | |
75 | } | |
e6dc35ac FG |
76 | |
77 | if auth_id.is_token() { | |
78 | if let Ok(info) = self.user_cfg.lookup::<ApiToken>("token", &auth_id.to_string()) { | |
79 | if !info.enable.unwrap_or(true) { | |
80 | return false; | |
81 | } | |
82 | if let Some(expire) = info.expire { | |
83 | if expire > 0 && expire <= now() { | |
84 | return false; | |
85 | } | |
86 | } | |
87 | return true; | |
88 | } else { | |
89 | return false; | |
90 | } | |
91 | } | |
92 | ||
93 | return true; | |
423e6561 | 94 | } |
a737179e DM |
95 | |
96 | pub fn check_privs( | |
97 | &self, | |
e6dc35ac | 98 | auth_id: &Authid, |
a737179e DM |
99 | path: &[&str], |
100 | required_privs: u64, | |
101 | partial: bool, | |
102 | ) -> Result<(), Error> { | |
e6dc35ac | 103 | let privs = self.lookup_privs(&auth_id, path); |
a737179e | 104 | let allowed = if partial { |
e6dc35ac | 105 | (privs & required_privs) != 0 |
a737179e | 106 | } else { |
e6dc35ac | 107 | (privs & required_privs) == required_privs |
a737179e DM |
108 | }; |
109 | if !allowed { | |
3cfc56f5 TL |
110 | // printing the path doesn't leaks any information as long as we |
111 | // always check privilege before resource existence | |
112 | bail!("no permissions on '/{}'", path.join("/")); | |
a737179e DM |
113 | } |
114 | Ok(()) | |
115 | } | |
423e6561 | 116 | |
e6dc35ac FG |
117 | pub fn is_superuser(&self, auth_id: &Authid) -> bool { |
118 | !auth_id.is_token() && auth_id.user() == "root@pam" | |
423e6561 DM |
119 | } |
120 | ||
e7cb4dc5 | 121 | pub fn is_group_member(&self, _userid: &Userid, _group: &str) -> bool { |
423e6561 DM |
122 | false |
123 | } | |
124 | ||
e6dc35ac | 125 | pub fn lookup_privs(&self, auth_id: &Authid, path: &[&str]) -> u64 { |
babab85b FG |
126 | let (privs, _) = self.lookup_privs_details(auth_id, path); |
127 | privs | |
128 | } | |
129 | ||
130 | pub fn lookup_privs_details(&self, auth_id: &Authid, path: &[&str]) -> (u64, u64) { | |
e6dc35ac | 131 | if self.is_superuser(auth_id) { |
babab85b | 132 | return (ROLE_ADMIN, ROLE_ADMIN); |
e7cb4dc5 | 133 | } |
1347b115 | 134 | |
e6dc35ac | 135 | let roles = self.acl_tree.roles(auth_id, path); |
423e6561 | 136 | let mut privs: u64 = 0; |
babab85b FG |
137 | let mut propagated_privs: u64 = 0; |
138 | for (role, propagate) in roles { | |
3fff55b2 | 139 | if let Some((role_privs, _)) = ROLE_NAMES.get(role.as_str()) { |
babab85b FG |
140 | if propagate { |
141 | propagated_privs |= role_privs; | |
142 | } | |
423e6561 DM |
143 | privs |= role_privs; |
144 | } | |
145 | } | |
e6dc35ac FG |
146 | |
147 | if auth_id.is_token() { | |
148 | // limit privs to that of owning user | |
149 | let user_auth_id = Authid::from(auth_id.user().clone()); | |
150 | privs &= self.lookup_privs(&user_auth_id, path); | |
babab85b FG |
151 | let (owner_privs, owner_propagated_privs) = self.lookup_privs_details(&user_auth_id, path); |
152 | privs &= owner_privs; | |
153 | propagated_privs &= owner_propagated_privs; | |
e6dc35ac FG |
154 | } |
155 | ||
babab85b | 156 | (privs, propagated_privs) |
423e6561 | 157 | } |
babab85b | 158 | |
423e6561 | 159 | } |
e7cb4dc5 WB |
160 | |
161 | impl UserInformation for CachedUserInfo { | |
162 | fn is_superuser(&self, userid: &str) -> bool { | |
163 | userid == "root@pam" | |
164 | } | |
165 | ||
166 | fn is_group_member(&self, _userid: &str, _group: &str) -> bool { | |
167 | false | |
168 | } | |
169 | ||
e6dc35ac FG |
170 | fn lookup_privs(&self, auth_id: &str, path: &[&str]) -> u64 { |
171 | match auth_id.parse::<Authid>() { | |
172 | Ok(auth_id) => Self::lookup_privs(self, &auth_id, path), | |
e7cb4dc5 WB |
173 | Err(_) => 0, |
174 | } | |
175 | } | |
176 | } |