1use dolang_winterop::security::{Sid, TokenGroupAttributes};
4use serde::{Deserialize, Serialize};
5#[cfg(unix)]
6use std::io;
7
8use crate::error::Result;
9pub use crate::{
10 macos_acl::{MacosAce, MacosAceFlags, MacosAceMask, MacosAceType, MacosAcl},
11 nfs4_acl::{Nfs4Ace, Nfs4AceFlags, Nfs4AceMask, Nfs4AceQualifier, Nfs4AceType, Nfs4Acl},
12 posix_acl::{PosixAce, PosixAcl, PosixAclError, PosixAclQualifier},
13};
14
15bitflags::bitflags! {
16 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
19 pub struct Permission: u8 {
20 const READ = 0o4;
22 const WRITE = 0o2;
24 const EXECUTE = 0o1;
26 }
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
32#[non_exhaustive]
33pub enum AclKind {
34 Posix,
36 Nfs4,
38 Macos,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
48#[non_exhaustive]
49pub enum Acl {
50 Posix(PosixAcl),
52 Nfs4(Nfs4Acl),
54 Macos(MacosAcl),
56}
57
58impl Acl {
59 pub fn kind(&self) -> AclKind {
61 match self {
62 Self::Posix(_) => AclKind::Posix,
63 Self::Nfs4(_) => AclKind::Nfs4,
64 Self::Macos(_) => AclKind::Macos,
65 }
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73#[non_exhaustive]
74pub enum PrincipalId {
75 Uid(u32),
77 Gid(u32),
79 Uuid(uuid::Uuid),
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
86#[non_exhaustive]
87pub enum PrincipalIdKind {
88 Uid,
90 Gid,
92 Uuid,
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98#[non_exhaustive]
99pub enum OwnershipIdentity {
100 Id(u32),
102 Name(String),
104 Sid(Sid),
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110#[serde(transparent)]
111pub struct SecurityInfo(SecurityInfoInner);
112
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114enum SecurityInfoInner {
115 Unix(UnixSecurityInfo),
117 Windows(WindowsTokenInfo),
119}
120
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub struct UnixSecurityInfo {
124 pub(crate) uid: u32,
126 pub(crate) gid: u32,
128 pub(crate) euid: u32,
130 pub(crate) egid: u32,
132 pub(crate) groups: Vec<u32>,
134}
135
136#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
138pub struct WindowsTokenInfo {
139 pub(crate) is_elevated: bool,
141 pub(crate) user_sid: Sid,
143 pub(crate) owner_sid: Sid,
145 pub(crate) primary_group_sid: Sid,
147 pub(crate) groups: Vec<TokenGroup>,
149}
150
151#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
153pub struct TokenGroup {
154 pub(crate) sid: Sid,
156 pub(crate) attributes: TokenGroupAttributes,
158}
159
160#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
162#[non_exhaustive]
163pub enum SidNameUse {
164 User,
166 Group,
168 Domain,
170 Alias,
172 WellKnownGroup,
174 DeletedAccount,
176 Invalid,
178 Unknown,
180 Computer,
182 Label,
184 LogonSession,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
190pub struct SidName {
191 pub(crate) sid: Sid,
193 pub(crate) name: String,
195 pub(crate) domain: String,
197 pub(crate) kind: SidNameUse,
199}
200
201impl SecurityInfo {
202 pub fn unix(&self) -> Option<&UnixSecurityInfo> {
204 match &self.0 {
205 SecurityInfoInner::Unix(info) => Some(info),
206 _ => None,
207 }
208 }
209 pub fn windows(&self) -> Option<&WindowsTokenInfo> {
211 match &self.0 {
212 SecurityInfoInner::Windows(info) => Some(info),
213 _ => None,
214 }
215 }
216 pub fn current() -> Result<Self> {
218 #[cfg(unix)]
219 return Ok(Self(SecurityInfoInner::Unix(UnixSecurityInfo::current()?)));
220 #[cfg(windows)]
221 return Ok(Self(SecurityInfoInner::Windows(
222 WindowsTokenInfo::current()?
223 )));
224 }
225}
226
227impl UnixSecurityInfo {
228 pub const fn uid(&self) -> u32 {
230 self.uid
231 }
232 pub const fn gid(&self) -> u32 {
234 self.gid
235 }
236 pub const fn effective_uid(&self) -> u32 {
238 self.euid
239 }
240 pub const fn effective_gid(&self) -> u32 {
242 self.egid
243 }
244 pub fn groups(&self) -> &[u32] {
246 &self.groups
247 }
248}
249
250impl TokenGroup {
251 pub fn sid(&self) -> &Sid {
253 &self.sid
254 }
255 pub const fn attributes(&self) -> TokenGroupAttributes {
257 self.attributes
258 }
259}
260
261impl SidName {
262 pub fn sid(&self) -> &Sid {
264 &self.sid
265 }
266 pub fn name(&self) -> &str {
268 &self.name
269 }
270 pub fn domain(&self) -> &str {
272 &self.domain
273 }
274 pub const fn kind(&self) -> SidNameUse {
276 self.kind
277 }
278}
279
280#[cfg(unix)]
281impl UnixSecurityInfo {
282 fn current() -> Result<Self> {
283 use nix::unistd::{getegid, geteuid, getgid, getuid};
284
285 let euid = geteuid();
286 let egid = getegid();
287
288 Ok(Self {
289 uid: getuid().as_raw(),
290 gid: getgid().as_raw(),
291 euid: euid.as_raw(),
292 egid: egid.as_raw(),
293 groups: current_groups(euid, egid)?,
294 })
295 }
296}
297
298#[cfg(all(unix, not(target_os = "macos")))]
299fn current_groups(_euid: nix::unistd::Uid, _egid: nix::unistd::Gid) -> Result<Vec<u32>> {
300 Ok(nix::unistd::getgroups()
301 .map_err(io::Error::from)?
302 .into_iter()
303 .map(|gid| gid.as_raw())
304 .collect())
305}
306
307#[cfg(target_os = "macos")]
308fn current_groups(euid: nix::unistd::Uid, egid: nix::unistd::Gid) -> Result<Vec<u32>> {
309 use std::{ffi::CString, ptr, slice};
310
311 unsafe extern "C" {
315 fn getgrouplist_2(
316 name: *const libc::c_char,
317 base_gid: libc::gid_t,
318 groups: *mut *mut libc::gid_t,
319 ) -> i32;
320 }
321
322 let user = nix::unistd::User::from_uid(euid)
323 .map_err(io::Error::from)?
324 .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "effective user not found"))?;
325 let name = CString::new(user.name)
326 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "user name contains NUL"))?;
327 let mut groups = ptr::null_mut();
328 let count = unsafe { getgrouplist_2(name.as_ptr(), egid.as_raw(), &mut groups) };
329 if count < 0 {
330 if !groups.is_null() {
331 unsafe { libc::free(groups.cast()) };
332 }
333 return Err(io::Error::other("getgrouplist_2 failed").into());
334 }
335 if count == 0 {
336 if !groups.is_null() {
337 unsafe { libc::free(groups.cast()) };
338 }
339 return Ok(Vec::new());
340 }
341 if count > 0 && groups.is_null() {
342 return Err(io::Error::new(
343 io::ErrorKind::InvalidData,
344 "getgrouplist_2 returned a null group list",
345 )
346 .into());
347 }
348 let result = unsafe { slice::from_raw_parts(groups, count as usize) }.to_vec();
349 unsafe { libc::free(groups.cast()) };
350 Ok(result)
351}
352
353impl WindowsTokenInfo {
354 pub const fn is_elevated(&self) -> bool {
356 self.is_elevated
357 }
358 pub fn user_sid(&self) -> &Sid {
360 &self.user_sid
361 }
362 pub fn owner_sid(&self) -> &Sid {
364 &self.owner_sid
365 }
366 pub fn primary_group_sid(&self) -> &Sid {
368 &self.primary_group_sid
369 }
370 pub fn groups(&self) -> &[TokenGroup] {
372 &self.groups
373 }
374 pub fn logon_sid(&self) -> Option<&Sid> {
376 self.groups
377 .iter()
378 .find(|group| group.attributes.contains(TokenGroupAttributes::LOGON_ID))
379 .map(|group| &group.sid)
380 }
381}
382
383#[cfg(windows)]
384impl WindowsTokenInfo {
385 fn current() -> Result<Self> {
386 use windows_sys::Win32::System::Threading::GetCurrentProcess;
387
388 unsafe { Self::from_process_handle(GetCurrentProcess()) }
391 }
392
393 pub(crate) unsafe fn from_process_handle(
400 handle: windows_sys::Win32::Foundation::HANDLE,
401 ) -> Result<Self> {
402 use std::{
403 io, mem,
404 os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle},
405 ptr, slice,
406 };
407 use windows_sys::Win32::{
408 Foundation::HANDLE,
409 Security::{
410 GetLengthSid, GetTokenInformation, IsValidSid, PSID, TOKEN_ELEVATION, TOKEN_GROUPS,
411 TOKEN_INFORMATION_CLASS, TOKEN_OWNER, TOKEN_PRIMARY_GROUP, TOKEN_QUERY, TOKEN_USER,
412 TokenElevation, TokenGroups, TokenOwner, TokenPrimaryGroup, TokenUser,
413 },
414 System::Threading::OpenProcessToken,
415 };
416
417 fn query(token: HANDLE, class: TOKEN_INFORMATION_CLASS) -> io::Result<Vec<usize>> {
418 let mut required = 0;
419 unsafe {
420 GetTokenInformation(token, class, ptr::null_mut(), 0, &mut required);
421 }
422 if required == 0 {
423 return Err(io::Error::last_os_error());
424 }
425 let word_size = mem::size_of::<usize>();
426 let mut buffer = vec![0usize; (required as usize).div_ceil(word_size)];
427 if unsafe {
428 GetTokenInformation(
429 token,
430 class,
431 buffer.as_mut_ptr().cast(),
432 required,
433 &mut required,
434 )
435 } == 0
436 {
437 return Err(io::Error::last_os_error());
438 }
439 Ok(buffer)
440 }
441
442 unsafe fn copy_sid(sid: PSID) -> io::Result<Sid> {
443 if sid.is_null() || unsafe { IsValidSid(sid) } == 0 {
444 return Err(io::Error::new(
445 io::ErrorKind::InvalidData,
446 "invalid token SID",
447 ));
448 }
449 let length = unsafe { GetLengthSid(sid) } as usize;
450 let bytes = unsafe { slice::from_raw_parts(sid.cast::<u8>(), length) };
451 Sid::from_bytes(bytes)
452 .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))
453 }
454
455 unsafe fn view<T>(buffer: &[usize]) -> &T {
456 unsafe { &*buffer.as_ptr().cast::<T>() }
457 }
458
459 let mut token = ptr::null_mut();
460 if unsafe { OpenProcessToken(handle, TOKEN_QUERY, &mut token) } == 0 {
461 return Err(io::Error::last_os_error().into());
462 }
463 let token = unsafe { OwnedHandle::from_raw_handle(token) };
464 let token = token.as_raw_handle();
465
466 let elevation = query(token, TokenElevation)?;
467 let user = query(token, TokenUser)?;
468 let owner = query(token, TokenOwner)?;
469 let primary_group = query(token, TokenPrimaryGroup)?;
470 let groups = query(token, TokenGroups)?;
471
472 let elevation = unsafe { view::<TOKEN_ELEVATION>(&elevation) };
473 let user = unsafe { copy_sid(view::<TOKEN_USER>(&user).User.Sid) }?;
474 let owner = unsafe { copy_sid(view::<TOKEN_OWNER>(&owner).Owner) }?;
475 let primary_group =
476 unsafe { copy_sid(view::<TOKEN_PRIMARY_GROUP>(&primary_group).PrimaryGroup) }?;
477 let groups_info = unsafe { view::<TOKEN_GROUPS>(&groups) };
478 let native_groups = unsafe {
479 slice::from_raw_parts(
480 groups_info.Groups.as_ptr(),
481 usize::try_from(groups_info.GroupCount).unwrap(),
482 )
483 };
484 let groups = native_groups
485 .iter()
486 .map(|group| {
487 Ok(TokenGroup {
488 sid: unsafe { copy_sid(group.Sid) }?,
489 attributes: TokenGroupAttributes::from_bits_retain(group.Attributes),
490 })
491 })
492 .collect::<io::Result<Vec<_>>>()?;
493
494 Ok(Self {
495 is_elevated: elevation.TokenIsElevated != 0,
496 user_sid: user,
497 owner_sid: owner,
498 primary_group_sid: primary_group,
499 groups,
500 })
501 }
502}