1use std::iter::once;
5use std::sync::Arc;
6
7use crate::event::{CreateEvent, ModifyEvent};
8use crate::plugins::Plugin;
9use crate::prelude::*;
10use crate::utils::uuid_to_gid_u32;
11
12const GID_SYSTEM_NUMBER_PREFIX: u32 = 0x7000_0000;
27const GID_SYSTEM_NUMBER_MASK: u32 = 0x0fff_ffff;
28
29pub const GID_REGULAR_USER_MIN: u32 = 1000;
33pub const GID_REGULAR_USER_MAX: u32 = 60000;
34
35pub const GID_UNUSED_A_MIN: u32 = 60578;
38pub const GID_UNUSED_A_MAX: u32 = 61183;
39
40pub const GID_UNUSED_B_MIN: u32 = 65520;
43pub const GID_UNUSED_B_MAX: u32 = 65533;
44
45pub const GID_UNUSED_C_MIN: u32 = 65536;
49const GID_UNUSED_C_MAX: u32 = 524287;
50
51const GID_NSPAWN_MIN: u32 = 524288;
54const GID_NSPAWN_MAX: u32 = 1879048191;
55
56const GID_UNUSED_D_MIN: u32 = 0x7000_0000;
57pub const GID_UNUSED_D_MAX: u32 = 0x7fff_ffff;
58
59pub struct GidNumber {}
64
65fn apply_gidnumber<T: Clone>(e: &mut Entry<EntryInvalid, T>) -> Result<(), OperationError> {
66 if (e.attribute_equality(Attribute::Class, &EntryClass::PosixGroup.into())
67 || e.attribute_equality(Attribute::Class, &EntryClass::PosixAccount.into()))
68 && !e.attribute_pres(Attribute::GidNumber)
69 {
70 let u_ref = e
71 .get_uuid()
72 .ok_or(OperationError::InvalidEntryState)
73 .inspect_err(|_e| {
74 admin_error!("Invalid Entry State - Missing UUID");
75 })?;
76
77 let gid = uuid_to_gid_u32(u_ref);
78
79 let gid = gid & GID_SYSTEM_NUMBER_MASK;
82 let gid = gid | GID_SYSTEM_NUMBER_PREFIX;
83
84 let gid_v = Value::new_uint32(gid);
85 admin_info!("Generated {} for {:?}", gid, u_ref);
86 e.set_ava(&Attribute::GidNumber, once(gid_v));
87 Ok(())
88 } else if let Some(gid) = e.get_ava_single_uint32(Attribute::GidNumber) {
89 if (GID_REGULAR_USER_MIN..=GID_REGULAR_USER_MAX).contains(&gid)
91 || (GID_UNUSED_A_MIN..=GID_UNUSED_A_MAX).contains(&gid)
92 || (GID_UNUSED_B_MIN..= GID_UNUSED_B_MAX).contains(&gid)
93 || (GID_UNUSED_C_MIN..=GID_UNUSED_C_MAX).contains(&gid)
94 || (GID_NSPAWN_MIN..=GID_NSPAWN_MAX).contains(&gid)
102 || (GID_UNUSED_D_MIN..=GID_UNUSED_D_MAX).contains(&gid)
103 {
104 Ok(())
105 } else {
106 error!(
109 "Requested GID ({}) overlaps a system range. Allowed ranges are {} to {}, {} to {} and {} to {}",
110 gid,
111 GID_REGULAR_USER_MIN, GID_REGULAR_USER_MAX,
112 GID_UNUSED_C_MIN, GID_UNUSED_C_MAX,
113 GID_UNUSED_D_MIN, GID_UNUSED_D_MAX
114 );
115 Err(OperationError::PL0001GidOverlapsSystemRange)
116 }
117 } else {
118 Ok(())
119 }
120}
121
122impl Plugin for GidNumber {
123 fn id() -> &'static str {
124 "plugin_gidnumber"
125 }
126
127 #[instrument(level = "debug", name = "gidnumber_pre_create_transform", skip_all)]
128 fn pre_create_transform(
129 _qs: &mut QueryServerWriteTransaction,
130 cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
131 _ce: &CreateEvent,
132 ) -> Result<(), OperationError> {
133 cand.iter_mut().try_for_each(apply_gidnumber)
134 }
135
136 #[instrument(level = "debug", name = "gidnumber_pre_modify", skip_all)]
137 fn pre_modify(
138 _qs: &mut QueryServerWriteTransaction,
139 _pre_cand: &[Arc<EntrySealedCommitted>],
140 cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
141 _me: &ModifyEvent,
142 ) -> Result<(), OperationError> {
143 cand.iter_mut().try_for_each(apply_gidnumber)
144 }
145
146 #[instrument(level = "debug", name = "gidnumber_pre_batch_modify", skip_all)]
147 fn pre_batch_modify(
148 _qs: &mut QueryServerWriteTransaction,
149 _pre_cand: &[Arc<EntrySealedCommitted>],
150 cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
151 _me: &BatchModifyEvent,
152 ) -> Result<(), OperationError> {
153 cand.iter_mut().try_for_each(apply_gidnumber)
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::{
160 GID_REGULAR_USER_MAX, GID_REGULAR_USER_MIN, GID_UNUSED_A_MAX, GID_UNUSED_A_MIN,
161 GID_UNUSED_B_MAX, GID_UNUSED_B_MIN, GID_UNUSED_C_MIN, GID_UNUSED_D_MAX,
162 };
163 use crate::prelude::*;
164
165 #[qs_test]
166 async fn test_gidnumber_generate(server: &QueryServer) {
167 let mut server_txn = server.write(duration_from_epoch_now()).await.expect("txn");
168
169 {
171 let user_a_uuid = uuid!("83a0927f-3de1-45ec-bea0-2f7b997ef244");
172 let op_result = server_txn.internal_create(vec![entry_init!(
173 (Attribute::Class, EntryClass::Account.to_value()),
174 (Attribute::Class, EntryClass::PosixAccount.to_value()),
175 (Attribute::Name, Value::new_iname("testperson_1")),
176 (Attribute::Uuid, Value::Uuid(user_a_uuid)),
177 (Attribute::Description, Value::new_utf8s("testperson")),
178 (Attribute::DisplayName, Value::new_utf8s("testperson"))
179 )]);
180
181 assert!(op_result.is_ok());
182
183 let user_a = server_txn
184 .internal_search_uuid(user_a_uuid)
185 .expect("Unable to access user");
186
187 let user_a_uid = user_a
188 .get_ava_single_uint32(Attribute::GidNumber)
189 .expect("gidnumber not present on account");
190
191 assert_eq!(user_a_uid, 0x797ef244);
192 }
193
194 let user_b_uuid = uuid!("d90fb0cb-6785-4f36-94cb-e364d9c13255");
196 {
197 let op_result = server_txn.internal_create(vec![entry_init!(
198 (Attribute::Class, EntryClass::Account.to_value()),
199 (Attribute::Class, EntryClass::PosixAccount.to_value()),
200 (Attribute::Name, Value::new_iname("testperson_2")),
201 (Attribute::Uuid, Value::Uuid(user_b_uuid)),
202 (Attribute::GidNumber, Value::Uint32(10001)),
203 (Attribute::Description, Value::new_utf8s("testperson")),
204 (Attribute::DisplayName, Value::new_utf8s("testperson"))
205 )]);
206
207 assert!(op_result.is_ok());
208
209 let user_b = server_txn
210 .internal_search_uuid(user_b_uuid)
211 .expect("Unable to access user");
212
213 let user_b_uid = user_b
214 .get_ava_single_uint32(Attribute::GidNumber)
215 .expect("gidnumber not present on account");
216
217 assert_eq!(user_b_uid, 10001);
218 }
219
220 {
222 let modlist = modlist!([m_purge(Attribute::GidNumber)]);
223 server_txn
224 .internal_modify_uuid(user_b_uuid, &modlist)
225 .expect("Unable to modify user");
226
227 let user_b = server_txn
228 .internal_search_uuid(user_b_uuid)
229 .expect("Unable to access user");
230
231 let user_b_uid = user_b
232 .get_ava_single_uint32(Attribute::GidNumber)
233 .expect("gidnumber not present on account");
234
235 assert_eq!(user_b_uid, 0x79c13255);
236 }
237
238 let user_c_uuid = uuid!("0d5086b0-74f9-4518-92b4-89df0c55971b");
239 {
242 let op_result = server_txn.internal_create(vec![entry_init!(
243 (Attribute::Class, EntryClass::Account.to_value()),
244 (Attribute::Class, EntryClass::Person.to_value()),
245 (Attribute::Name, Value::new_iname("testperson_3")),
246 (Attribute::Uuid, Value::Uuid(user_c_uuid)),
247 (Attribute::Description, Value::new_utf8s("testperson")),
248 (Attribute::DisplayName, Value::new_utf8s("testperson"))
249 )]);
250
251 assert!(op_result.is_ok());
252
253 let user_c = server_txn
254 .internal_search_uuid(user_c_uuid)
255 .expect("Unable to access user");
256
257 assert_eq!(user_c.get_ava_single_uint32(Attribute::GidNumber), None);
258
259 let modlist = modlist!([m_pres(
260 Attribute::Class,
261 &EntryClass::PosixAccount.to_value()
262 )]);
263 server_txn
264 .internal_modify_uuid(user_c_uuid, &modlist)
265 .expect("Unable to modify user");
266
267 let user_c = server_txn
268 .internal_search_uuid(user_c_uuid)
269 .expect("Unable to access user");
270
271 let user_c_uid = user_c
272 .get_ava_single_uint32(Attribute::GidNumber)
273 .expect("gidnumber not present on account");
274
275 assert_eq!(user_c_uid, 0x7c55971b);
276 }
277
278 let user_d_uuid = uuid!("36dc9010-d80c-404b-b5ba-8f66657c2f1d");
279 {
282 let op_result = server_txn.internal_create(vec![entry_init!(
283 (Attribute::Class, EntryClass::Account.to_value()),
284 (Attribute::Class, EntryClass::Person.to_value()),
285 (Attribute::Name, Value::new_iname("testperson_4")),
286 (Attribute::Uuid, Value::Uuid(user_d_uuid)),
287 (Attribute::Description, Value::new_utf8s("testperson")),
288 (Attribute::DisplayName, Value::new_utf8s("testperson"))
289 )]);
290
291 assert!(op_result.is_ok());
292
293 let user_d = server_txn
294 .internal_search_uuid(user_d_uuid)
295 .expect("Unable to access user");
296
297 assert_eq!(user_d.get_ava_single_uint32(Attribute::GidNumber), None);
298
299 let modlist = modlist!([m_pres(
300 Attribute::Class,
301 &EntryClass::PosixAccount.to_value()
302 )]);
303 server_txn
304 .internal_modify_uuid(user_d_uuid, &modlist)
305 .expect("Unable to modify user");
306
307 let user_d = server_txn
308 .internal_search_uuid(user_d_uuid)
309 .expect("Unable to access user");
310
311 let user_d_uid = user_d
312 .get_ava_single_uint32(Attribute::GidNumber)
313 .expect("gidnumber not present on account");
314
315 assert_eq!(user_d_uid, 0x757c2f1d);
316 }
317
318 let user_e_uuid = uuid!("a6dc0d68-9c7a-4dad-b1e2-f6274b691373");
319 {
322 let op_result = server_txn.internal_create(vec![entry_init!(
323 (Attribute::Class, EntryClass::Account.to_value()),
324 (Attribute::Class, EntryClass::Person.to_value()),
325 (Attribute::Name, Value::new_iname("testperson_5")),
326 (Attribute::Uuid, Value::Uuid(user_e_uuid)),
327 (Attribute::Description, Value::new_utf8s("testperson")),
328 (Attribute::DisplayName, Value::new_utf8s("testperson"))
329 )]);
330
331 assert!(op_result.is_ok());
332
333 let user_e = server_txn
334 .internal_search_uuid(user_e_uuid)
335 .expect("Unable to access user");
336
337 assert_eq!(user_e.get_ava_single_uint32(Attribute::GidNumber), None);
338
339 let modlist = modlist!([
340 m_pres(Attribute::Class, &EntryClass::PosixAccount.to_value()),
341 m_pres(Attribute::GidNumber, &Value::Uint32(10002))
342 ]);
343 server_txn
344 .internal_modify_uuid(user_e_uuid, &modlist)
345 .expect("Unable to modify user");
346
347 let user_e = server_txn
348 .internal_search_uuid(user_e_uuid)
349 .expect("Unable to access user");
350
351 let user_e_uid = user_e
352 .get_ava_single_uint32(Attribute::GidNumber)
353 .expect("gidnumber not present on account");
354
355 assert_eq!(user_e_uid, 10002);
356 }
357
358 let user_f_uuid = uuid!("33afc396-2434-47e5-b143-05176148b50e");
360 {
363 let op_result = server_txn.internal_create(vec![entry_init!(
364 (Attribute::Class, EntryClass::Account.to_value()),
365 (Attribute::Class, EntryClass::Person.to_value()),
366 (Attribute::Name, Value::new_iname("testperson_6")),
367 (Attribute::Uuid, Value::Uuid(user_f_uuid)),
368 (Attribute::Description, Value::new_utf8s("testperson")),
369 (Attribute::DisplayName, Value::new_utf8s("testperson"))
370 )]);
371
372 assert!(op_result.is_ok());
373
374 for id in [
375 0,
376 500,
377 GID_REGULAR_USER_MIN - 1,
378 GID_REGULAR_USER_MAX + 1,
379 GID_UNUSED_A_MIN - 1,
380 GID_UNUSED_A_MAX + 1,
381 GID_UNUSED_B_MIN - 1,
382 GID_UNUSED_B_MAX + 1,
383 GID_UNUSED_C_MIN - 1,
384 GID_UNUSED_D_MAX + 1,
385 u32::MAX,
386 ] {
387 let modlist = modlist!([
388 m_pres(Attribute::Class, &EntryClass::PosixAccount.to_value()),
389 m_pres(Attribute::GidNumber, &Value::Uint32(id))
390 ]);
391 let op_result = server_txn.internal_modify_uuid(user_f_uuid, &modlist);
392
393 trace!(?id);
394 assert_eq!(op_result, Err(OperationError::PL0001GidOverlapsSystemRange));
395 }
396 }
397
398 assert!(server_txn.commit().is_ok());
399 }
400}